﻿using PmSoft.Core;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using Microsoft.Extensions.DependencyInjection;
using PmSoft.Core.Domain.Auth;

namespace PmSoft.Web.Abstractions.RealTime;

/// <summary>
/// 基于内存的在线客户端存储实现，用于管理实时连接的客户端。
/// </summary>
public class InMemoryOnlineClientStore : IOnlineClientStore
{
	/// <summary>
	/// 存储在线客户端的线程安全字典，以连接 ID 作为键。
	/// </summary>
	protected ConcurrentDictionary<string, IOnlineClient> Clients { get; }

	/// <summary>
	/// 初始化 <see cref="InMemoryOnlineClientStore"/> 的新实例。
	/// </summary>
	public InMemoryOnlineClientStore()
	{
		Clients = new ConcurrentDictionary<string, IOnlineClient>();
	}

	/// <summary>
	/// 异步添加在线客户端到存储中。
	/// </summary>
	/// <param name="client">要添加的在线客户端对象。</param>
	/// <returns>表示异步操作已完成的任务。</returns>
	public Task AddAsync(IOnlineClient client)
	{
		Clients.AddOrUpdate(client.ConnectionId, client, (_, _) => client);
		return Task.CompletedTask;
	}

	/// <summary>
	/// 异步移除指定连接 ID 的客户端。
	/// </summary>
	/// <param name="connectionId">客户端的连接 ID。</param>
	/// <returns>如果客户端成功移除则返回 true，否则返回 false。</returns>
	public Task<bool> RemoveAsync(string connectionId)
	{
		return TryRemoveAsync(connectionId, _ => { });
	}

	/// <summary>
	/// 尝试移除指定连接 ID 的客户端，并对移除的客户端执行自定义操作。
	/// </summary>
	/// <param name="connectionId">客户端的连接 ID。</param>
	/// <param name="configureClient">对移除的客户端执行的配置操作。</param>
	/// <returns>如果客户端存在并成功移除则返回 true，否则返回 false。</returns>
	public Task<bool> TryRemoveAsync(string connectionId, Action<IOnlineClient> configureClient)
	{
		bool hasRemoved = Clients.TryRemove(connectionId, out IOnlineClient? client);
		if (hasRemoved && client != null)
		{
			configureClient(client);
		}
		return Task.FromResult(hasRemoved);
	}

	/// <summary>
	/// 尝试获取指定连接 ID 的客户端，并对客户端执行自定义操作。
	/// </summary>
	/// <param name="connectionId">客户端的连接 ID。</param>
	/// <param name="configureClient">对获取到的客户端执行的配置操作。</param>
	/// <returns>如果客户端存在则返回 true，否则返回 false。</returns>
	public Task<bool> TryGetAsync(string connectionId, Action<IOnlineClient> configureClient)
	{
		bool hasValue = Clients.TryGetValue(connectionId, out IOnlineClient? client);
		if (hasValue && client != null)
		{
			configureClient(client);
		}
		return Task.FromResult(hasValue);
	}

	/// <summary>
	/// 检查存储中是否包含指定连接 ID 的客户端。
	/// </summary>
	/// <param name="connectionId">客户端的连接 ID。</param>
	/// <returns>如果存储中存在该连接 ID 的客户端则返回 true，否则返回 false。</returns>
	public Task<bool> ContainsAsync(string connectionId)
	{
		bool hasKey = Clients.ContainsKey(connectionId);
		return Task.FromResult(hasKey);
	}

	/// <summary>
	/// 异步获取所有在线客户端的只读列表。
	/// </summary>
	/// <returns>包含所有在线客户端的只读列表。</returns>
	public Task<IReadOnlyList<IOnlineClient>> GetAllAsync()
	{
		return Task.FromResult<IReadOnlyList<IOnlineClient>>(Clients.Values.ToImmutableList());
	}

	/// <summary>
	/// 异步获取指定用户标识的所有在线客户端。
	/// </summary>
	/// <param name="userIdentifier">包含用户 ID 的用户对象。</param>
	/// <returns>与指定用户关联的所有在线客户端的只读列表。</returns>
	public async Task<IReadOnlyList<IOnlineClient>> GetAllByUserIdAsync(IUserIdentifier userIdentifier)
	{
		return (await GetAllAsync())
			  .Where(c => c.UserId == userIdentifier.UserId && c.TenantType == userIdentifier.TenantType)
			  .ToImmutableList();
	}
}